/**
 * 
 */
package quasiMonteCarlo;

import monteCarlo.MonteCarlo;
import monteCarlo.interfaces.Function;

import org.apache.log4j.Logger;

import umontreal.iro.lecuyer.rng.RandomStream;

/**
 * @author Propriétaire
 * 
 */
public class QuasiMonteCarlo {
	
	public static Logger	LOG	= Logger.getLogger(MonteCarlo.class);
	
	
	public static void skipFirstPoints(RandomStream stream, int n, int dim) {
		stream.resetStartStream();
		
		for(int i=1; i <= n; i++) {
			stream.nextDouble();
			
			if(i % dim == 0) {
				stream.resetNextSubstream();
			}
		}
		
		stream.resetStartSubstream();
	}
	
	public static Double quasiMonteCarlo(RandomStream stream, Integer n, Integer nbParams,
	        Double a, Double b, Function func) {
		
		// LOG.debug("Invoking Quasi Monte Carlo process");
		
		Double[] X = new Double[nbParams];
		Double uN = new Double(0);
		Double uNSquare = new Double(0);
		Double approximation = new Double(0);
		Double error = new Double(0);
		Double[] results = new Double[n];
		
		// LOG.debug("Generation of " + n + " Uniform variable");
		try {
			
			for (int cpt = 0; cpt < n; cpt++) {
				for (int i = 0; i < nbParams; i++) {
					X[i] = stream.nextDouble() * (b - a) + a;
				}
				results[cpt] = func.f(X);
				
				uN += results[cpt];
				
				stream.resetNextSubstream();
			}
			
			// LOG.debug("Calculating the average");
			uN *= (1.0 / n);
			
			for (int cpt = 0; cpt < n; cpt++) {
				uNSquare += Math.pow(results[cpt] - uN, 2);
			}
			
			uNSquare *= (1.0 / n);
			
			approximation = (b - a) * uN;
			
			error = Math.pow((1.0 - uN), 2);
			// (b - a) * Math.sqrt(uNSquare / n);
			
			// LOG.debug("approximation : " + approximation);
			// LOG.debug("error : " + error);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return error;
		
	}
	
}
